﻿// Copyright 2007-2009 Stuart Caborn, Alex Scordellis
//  
//  Licensed under the Apache License, Version 2.0 (the "License"); 
//  http://www.apache.org/licenses/LICENSE-2.0

using System.Linq;
using System.Reflection;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;
using SampleApp.MonitoredGenerics;

namespace NSynthesis.Test.Wired
{
    [TestFixture]
    public class MockedMethodCallTest_Generics
    {
        #region Setup/Teardown

        [SetUp]
        public void SetUp()
        {
            Synthesis.Reset();
            mocks = new MockRepository();
        }

        #endregion

        private MockRepository mocks;

        [Test]
        public void a_mock_call_to_a_generic_interface_registers_a_closed_concrete_implementation()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(123);

            var realImplementations = Synthesis.Recorder.MockedMethodCalls.First().RealImplementations;
            Assert.That(
                realImplementations.Count(method => Helpers.MethodInfoIn(method).Equals(typeof(CustomerRepository).GetMethod("Load"))),
                Is.EqualTo(1),
                "Expected to find the CustomerRepository implementation");
        }

        [Test]
        public void a_mock_call_to_a_generic_interface_should_not_register_a_closed_concrete_implementation_with_the_wrong_type_parameter()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(123);

            var realImplementations = Synthesis.Recorder.MockedMethodCalls.First().RealImplementations;
            Assert.That(
                realImplementations.Count(method => Helpers.MethodInfoIn(method).Equals(typeof(WidgetRepository).GetMethod("Load"))),
                Is.EqualTo(0),
                "Did not expect to find the WidgetRepository implementation");
        }

        [Test]
        public void a_mock_call_to_a_generic_interface_registers_a_generic_concrete_implementation()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(123);

            var realImplementations = Synthesis.Recorder.MockedMethodCalls.First().RealImplementations;
            Assert.That(
                realImplementations.Count(method => Helpers.MethodInfoIn(method).Equals(typeof(GenericConcreteRepository<>).GetMethod("Load"))),
                Is.EqualTo(1),
                "Expected to find the GenericConcreteRepository implementation");
        }

        /// <summary>
        /// These tests prove that what PostSharp gives us in a Wired test, we can create in a Standalone test using
        /// calls to typeof(), GetType() etc. We can then confidently use those calls in Standalone tests of our matching
        /// logic.
        /// </summary>

        #region Demonstrations of what PostSharp gives us

        [Test]
        public void a_mock_call_to_a_generic_interface_gives_what_we_expect()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(123);
            var mockedMethodCall = Synthesis.Recorder.MockedMethodCalls.First();
            var invokedMockCall = (MethodInfo) mockedMethodCall.GetType().GetField("invokedMockCall", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(mockedMethodCall);

            Assert.That(invokedMockCall.Equals(mocks.CreateMock<IRepository<Customer>>().GetType().GetMethod("Load")));
        }

        [Test]
        public void a_mock_call_to_a_closure_of_a_concrete_generic_class_gives_what_we_expect()
        {
            mocks.CreateMock<GenericClass<Foo>>().Method();

            var mockedMethodCall = Synthesis.Recorder.MockedMethodCalls.First();
            var invokedMockCall = (MethodInfo) mockedMethodCall.GetType().GetField("invokedMockCall", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(mockedMethodCall);

            Assert.That(invokedMockCall.Equals(mocks.CreateMock<GenericClass<Foo>>().GetType().GetMethod("Method")));
        }

        [Test]
        public void a_mock_call_to_a_closure_of_an_abstract_generic_class_gives_what_we_expect()
        {
            mocks.CreateMock<AbstractClass<Foo>>().Method();

            var mockedMethodCall = Synthesis.Recorder.MockedMethodCalls.First();
            var invokedMockCall = (MethodInfo) mockedMethodCall.GetType().GetField("invokedMockCall", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(mockedMethodCall);

            Assert.That(invokedMockCall.Equals(mocks.CreateMock<AbstractClass<Foo>>().GetType().GetMethod("Method")));
        }

        [Test]
        public void a_mock_call_to_a_closure_of_a_generic_method_gives_us_what_we_expect()
        {
            mocks.CreateMock<ClassWithGenericMethods>().Foo(12);
            var mockedMethodCall = Synthesis.Recorder.MockedMethodCalls.First();
            var invokedMockCall = (MethodInfo)mockedMethodCall.GetType().GetField("invokedMockCall", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(mockedMethodCall);

            Assert.That(invokedMockCall, Is.EqualTo(mocks.CreateMock<ClassWithGenericMethods>().GetType().GetMethod("Foo").MakeGenericMethod(typeof(int))));
        }

        #endregion
    }
}